module TableForHelper
  class Builder
    include ActionView::Helpers::TagHelper

    # Initialize variables
    # Options:
    # model - collection class
    # ...other - html options for the table
    def initialize collection, options = nil
      @collection = collection
      @head = {}
      @body = {}
      @footer = {}
      @options = {}
      @table_options = options.symbolize_keys

      if @table_options.has_key?(:model)
        @model = options[:model]
        @table_options.delete :model
      else
        @model = collection.first.class
      end

      yield self if block_given?
    end

    # Generate a html
    def to_html
      # Add all model attributes if no columns configured
      @model.attribute_names.each{|name| column(name)} if @body.empty?

      table, thead, tfooter = '', '', ''

      # Construct body html rows
      @collection.each do |resource|
        row = ''
        @body.each do |column, value|
          value ||= resource[column]
          value = value.call(resource, column) if value.is_a? Proc
          row << content_tag('td', value, @options[column][:body_options])
        end
        table << content_tag('tr', row.html_safe)
      end

      # Construct head and footer html rows
      @body.keys.each do |column|
        @head[column] ||= title column if @head.has_key? column
        @head[column] = @head[column].call(@collection, column) if @head[:column].is_a? Proc
        thead << content_tag('th', @head[column], @options[column][:head_options]) if !@head.empty?
        @footer[column] = @footer[column].call(@collection, column) if @footer[:column].is_a? Proc
        tfooter << content_tag('td', @footer[column], @options[column][:footer_options]) if !@footer.empty?
      end
      thead = content_tag('tr', thead.html_safe) if !thead.empty?
      tfooter = content_tag('tr', tfooter.html_safe) if !tfooter.empty?

      # Join all table parts, put it to a table tag and return
      table = content_tag 'tbody', table.html_safe
      table = content_tag('thead', thead) + table if !thead.empty?
      table << content_tag('tfoot', tfooter) if !tfooter.empty?

      content_tag('table', table.html_safe, @table_options)
    end

    def title column
      if column.is_a?(Class) && column.methods.include?(:model_name)
        if @model.methods.include?(:model_name)
          @model.model_name.human(options)
        else
          @model.to_s
        end
      else
        if @model.methods.include?(:human_attribute_name)
          @model.human_attribute_name(column)
        else
          column.to_s
        end
      end
    end

    def body column, value = nil, options = {}, &block
      options, value = value, nil if value.is_a? Hash
      @body[column] = (value.nil? and block_given?) ? block : value
      @options[column] ||= {}
      @options[column][:body_options] = options.is_a?(Hash) ? options.symbolize_keys : {}
    end

    def head column, value = nil, options = {}, &block
      options, value = value, nil if value.is_a? Hash
      @head[column] = (value.nil? and block_given?) ? block : value
      @options[column] ||= {}
      @options[column][:head_options] = options.is_a?(Hash) ? options.symbolize_keys : {}
    end

    def footer column, value = nil, options = {}, &block
      options, value = value, nil if value.is_a? Hash
      @footer[column] = (value.nil? and block_given?) ? block : value
      @options[column] ||= {}
      @options[column][:footer_options] = options.is_a?(Hash) ? options.symbolize_keys : {}
    end

    def column column, value = nil, options = {}, &block
      options, value = value, nil if value.is_a? Hash
      options = options.symbolize_keys

      options[:body] ||= block if block_given?
      @body[column] = options[:body]

      value ||= options[:head]
      @head[column] = value if value or options.has_key?(:head_options)

      @footer[column] = options[:footer] if options.has_key?(:footer) or options.has_key?(:footer_options)

      @options[column] = options
    end
  end
end
